﻿using System;
using System.Globalization;
using System.Threading;
using OpenTap.Plugins.Interfaces.Common;
using OpenTap.Plugins.Interfaces.Eload;

namespace OpenTap.Plugins.Eload
{
    [Display("Eload Chroma", Group: "OpenTap.Plugins", Description: "Driver for Chroma electronic load")]
    public class EloadChroma : ScpiInstrument, IEload
    {
        private const uint MinChannelNumber = 1;

        public EloadChroma()
        {
            Name = "Chroma";
        }

        public void Clear()
        {
            Log.Info("Eload:Clear()");
            ScpiCommand("*CLS");
        }

        public void Wait()
        {
            Log.Info("Eload:Wait()");
            ScpiCommand("*WAI");
        }

        public bool SystemError(out string response)
        {
            throw new NotImplementedException();
        }

        public void EloadReset()
        {
            Log.Info("Eload:EloadReset()");
            ScpiCommand("*RST");
        }

        public string GetVersion()
        {
            throw new NotImplementedException();
        }

        public void SetCurrent(long channel, double current)
        {
            SetChannel(channel);
            Log.Info($"ELoad current set to {current.ToString("0.00", CultureInfo.InvariantCulture)} A");
            ScpiCommand($"CURR:STAT:L1 {current.ToString(CultureInfo.InvariantCulture)}\n");
        }

        public void SetResistance(long channel, double resistance)
        {
            SetChannel(channel);
            Log.Info($"ELoad resistance set to {resistance.ToString("0.00", CultureInfo.InvariantCulture)} Ohm");
            ScpiCommand($"RES:STAT:L1 {resistance.ToString(CultureInfo.InvariantCulture)}\n");
        }

        public void ELoadOnOff(long channel, EState onOff)
        {
            SetChannel(channel);
            Log.Info($"ELoad state set to {onOff}");
            ScpiCommand($"LOAD {onOff.ToString().ToUpper()}\n");
        }

        public void SetMode(long channel, ELoadMode eLoadMode, double maxValueOfMeasurement)
        {
            SetChannel(channel);
            Log.Info($"ELoad mode set to {eLoadMode}");
            var id = ScpiQuery("CHAN:ID?\n");

            //Chroma modes:CCL,CCM,CCH,CRL,CRM,CRH,CVL,CVM,CVH,CPL,CPM,CPH,CZL,CZM,CZH,CCDL,CCDM,CCDH,
            //CCFSL,CCFSM,CCFSH,TIML,TIMM,TIMH,ACL,ACM,ACH,IRL,IRM,IRH,OCPL,OCPM,OCPH, PROG
            switch (eLoadMode)
            {
                case ELoadMode.ConstantCurrent:
                    if (id.Contains("63610-600-15"))
                    {
                        if (maxValueOfMeasurement <= 0.15)
                        {
                            ScpiCommand("MODE CCL\n");
                        }
                        else if (maxValueOfMeasurement > 0.15 && maxValueOfMeasurement <= 1.5)
                        {
                            ScpiCommand("MODE CCM\n");
                        }
                        else if (maxValueOfMeasurement > 1.5 && maxValueOfMeasurement <= 15.0)
                        {
                            ScpiCommand("MODE CCH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload current not in module limits: {id}current: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63610-80-20"))
                    {
                        if (maxValueOfMeasurement <= 0.2)
                        {
                            ScpiCommand("MODE CCL\n");
                        }
                        else if (maxValueOfMeasurement > 0.2 && maxValueOfMeasurement <= 2.0)
                        {
                            ScpiCommand("MODE CCM\n");
                        }
                        else if (maxValueOfMeasurement > 2.0 && maxValueOfMeasurement <= 20.0)
                        {
                            ScpiCommand("MODE CCH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload current not in module limits: {id}current: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63630-80-60"))
                    {
                        if (maxValueOfMeasurement <= 0.6)
                        {
                            ScpiCommand("MODE CCL\n");
                        }
                        else if (maxValueOfMeasurement > 0.6 && maxValueOfMeasurement <= 6.0)
                        {
                            ScpiCommand("MODE CCM\n");
                        }
                        else if (maxValueOfMeasurement > 6.0 && maxValueOfMeasurement <= 60.0)
                        {
                            ScpiCommand("MODE CCH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload current not in module limits: {id}current: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63640-80-80"))
                    {
                        if (maxValueOfMeasurement <= 0.8)
                        {
                            ScpiCommand("MODE CCL\n");
                        }
                        else if (maxValueOfMeasurement > 0.8 && maxValueOfMeasurement <= 8.0)
                        {
                            ScpiCommand("MODE CCM\n");
                        }
                        else if (maxValueOfMeasurement > 8.0 && maxValueOfMeasurement <= 80.0)
                        {
                            ScpiCommand("MODE CCH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload current not in module limits: {id}current: {maxValueOfMeasurement}");
                        }
                    }

                    break;

                case ELoadMode.ConstantResistance:
                    if (id.Contains("63610-600-15"))
                    {
                        if (maxValueOfMeasurement <= 270)
                        {
                            ScpiCommand("MODE CRL\n");
                        }
                        else if (maxValueOfMeasurement > 270 && maxValueOfMeasurement <= 4000)
                        {
                            ScpiCommand("MODE CRM\n");
                        }
                        else if (maxValueOfMeasurement > 4000 && maxValueOfMeasurement <= 200000)
                        {
                            ScpiCommand("MODE CRH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload resistance not in module limits: {id}resistance: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63610-80-20"))
                    {
                        if (maxValueOfMeasurement <= 80)
                        {
                            ScpiCommand("MODE CRL\n");
                        }
                        else if (maxValueOfMeasurement > 80 && maxValueOfMeasurement <= 2900)
                        {
                            ScpiCommand("MODE CRM\n");
                        }
                        else if (maxValueOfMeasurement > 2900 && maxValueOfMeasurement <= 12000)
                        {
                            ScpiCommand("MODE CRH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload resistance not in module limits: {id}resistance: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63630-80-60"))
                    {
                        if (maxValueOfMeasurement < 30)
                        {
                            ScpiCommand("MODE CRL\n");
                        }
                        else if (maxValueOfMeasurement > 30 && maxValueOfMeasurement <= 600)
                        {
                            ScpiCommand("MODE CRM\n");
                        }
                        else if (maxValueOfMeasurement > 600 && maxValueOfMeasurement <= 3000)
                        {
                            ScpiCommand("MODE CRH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload resistance not in module limits: {id}resistance: {maxValueOfMeasurement}");
                        }
                    }

                    if (id.Contains("63640-80-80"))
                    {
                        if (maxValueOfMeasurement < 20)
                        {
                            ScpiCommand("MODE CRL\n");
                        }
                        else if (maxValueOfMeasurement > 30 && maxValueOfMeasurement <= 720)
                        {
                            ScpiCommand("MODE CRM\n");
                        }
                        else if (maxValueOfMeasurement > 720 && maxValueOfMeasurement <= 2900)
                        {
                            ScpiCommand("MODE CRH\n");
                        }
                        else
                        {
                            throw new ApplicationException(
                                $"Eload resistance not in module limits: {id}resistance: {maxValueOfMeasurement}");
                        }
                    }

                    break;
                default:
                    throw new ApplicationException($"Eload Mode unknown: {eLoadMode}");
            }
        }

        public ELoadMode GetMode()
        {
            var eMode = ELoadMode.EloadModeUnknown;

            Log.Info("Get mode");
            var mode = ScpiQuery("MODE?\n");
            mode = mode.Remove(2, 1);

            switch (mode)
            {
                case "CC\n":
                    eMode = ELoadMode.ConstantCurrent;
                    break;
                case "CR\n":
                    eMode = ELoadMode.ConstantResistance;
                    break;
            }

            return eMode;
        }

        public void SetContinuousTransientModeOnOff(long channel, EState onOff)
        {
            throw new NotImplementedException();
        }

        public void SetupContinuousTransient(long channel, double mainCurrentA, double transientCurrentA, uint frequencyHz,
            uint dutyCyclePercentage)
        {
            throw new NotImplementedException();
        }

        public double MeasureCurrent(long channel)
        {
            SetChannel(channel);
            var current = ScpiQuery("MEAS:CURR?\n");
            Log.Info("ELoad measured current is {0:f3} A", Convert.ToDouble(current, CultureInfo.InvariantCulture));
            return Convert.ToDouble(current, CultureInfo.InvariantCulture);
        }

        public double MeasureVoltage(long channel)
        {
            var originalTimeOut = IoTimeout;
            IoTimeout = 5000;
            SetChannel(channel);
            Thread.Sleep(100);
            var voltage = ScpiQuery("MEAS:VOLT?\n");
            Log.Info("ELoad measured voltage is {0:f3} V", Convert.ToDouble(voltage, CultureInfo.InvariantCulture));
            IoTimeout = originalTimeOut;
            return Convert.ToDouble(voltage, CultureInfo.InvariantCulture);
        }

        public double MeasurePower(long channel)
        {
            SetChannel(channel);
            var power = ScpiQuery("MEAS:POW?\n");
            Log.Info("ELoad measured power is {0:f3} W", Convert.ToDouble(power, CultureInfo.InvariantCulture));
            return Convert.ToDouble(power, CultureInfo.InvariantCulture);
        }

        public double MeasureMaximumVoltage(long channel)
        {
            throw new NotImplementedException();
        }

        public double MeasureMinimumVoltage(long channel)
        {
            throw new NotImplementedException();
        }

        public void SetChannel(long channel)
        {
            Log.Info($"Set channel {channel}");
            //ScpiCommand("CHAN " + ReturnConfiguredLoad(channel) + "\n");
            if (channel < MinChannelNumber) throw new ArgumentOutOfRangeException(nameof(channel));
            ScpiCommand($"CHAN {channel}\n");
        }

        public long GetChannel()
        {
            Log.Info("Get channel number");
            var chan = ScpiQuery("CHAN?\n");
            Log.Info($"ELoad channel number is {chan}");
            return Convert.ToInt64(chan);
        }

        public void SetReadFromSense(long channel, bool sense)
        {
            if (sense) ScpiCommand("MEAS:INP UUT\n");
            else ScpiCommand("MEAS:INP LOAD\n");
        }

        public void SetTransientCurrent(long channel, double transientCurrent)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetContinuousTransientFrequency(long channel, uint frequencyHz)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetContinuousTransientDutyCycle(long channel, uint dutyCyclePercentage)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetTransientCurrentSlewRate(long channel, ESlewRateDirection slewRateDirect, double slewRate)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetTriggerSource(ETriggerSource source)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetTriggerTimer(double timerMs)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetTriggerDelay(long channel, uint delayMs)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void ResetTriggerSystem()
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void InitiateTriggerSequence(ETriggerSequence triggerSeq)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetContinuousTriggerInitiateOnOff(EState state)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SelectTransientMode(long channel, ETransientMode mode)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetTransientModeOnOff(long channel, EState onOff)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SelectTransientSource(long channel, ETransientSource source)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public void SetPulseTransientModeWidth(long channel, double pulseWidthMs)
        {
            // TODO: Check whether this is needed by the Chroma.
            throw new NotImplementedException();
        }

        public int SelfTest(out string message)
        {
            throw new NotImplementedException();
        }

        public int Selftest(out string message)
        {
            throw new NotImplementedException();
        }

        public void SetParallelInit(long channel, ELoadParallelInit onOff)
        {
            SetChannel(channel);
            if (GetParallelMode() != ELoadParallelMode.Master)
                throw new ArgumentException("Only parallel mode MASTER can set parallel init state.");
            Log.Info($"Set parallel init mode {onOff} for channel {channel}");
            ScpiCommand($"CONF:PARA:INIT {onOff}\n");
        }

        public void SetParallelMode(long channel, ELoadParallelMode parallelMode)
        {
            SetChannel(channel);
            Log.Info($"Set parallel mode {parallelMode} for channel {channel}");
            ScpiCommand($"CONF:PARA:MODE {parallelMode}\n");
        }

        public ELoadParallelMode GetParallelMode()
        {
            Log.Info("Get parallel mode");
            var mode = ScpiQuery("CONF:PARA:MODE?\n");
            var eMode = ELoadParallelMode.None;

            switch (mode.ToUpper())
            {
                case "MASTER\n":
                    eMode = ELoadParallelMode.Master;
                    break;
                case "SLAVE\n":
                    eMode = ELoadParallelMode.Slave;
                    break;
            }

            return eMode;
        }
    }
}